#ifndef HTTP_HPP
#define HTTP_HPP

#include "../include/TcpServer.h"
#include <algorithm>
#include <cctype>
#include <exception>
#include <fstream>
#include <limits>
#include <regex>
#include <sstream>
#include <string>
#include <sys/stat.h>
#include <unordered_map>
#include <vector>
constexpr size_t MAX_LINE = 8192;

class Util
{
private:
    static int HexValue(char ch)
    {
        if (ch >= '0' && ch <= '9')
            return ch - '0';
        if (ch >= 'A' && ch <= 'F')
            return ch - 'A' + 10;
        if (ch >= 'a' && ch <= 'f')
            return ch - 'a' + 10;
        return -1;
    }

    static bool HexPairToByte(char high, char low, unsigned char &out)
    {
        int hi = HexValue(high);
        int lo = HexValue(low);
        if (hi < 0 || lo < 0)
            return false;
        out = static_cast<unsigned char>((hi << 4) | lo);
        return true;
    }

public:
    // 字符串分隔函数
    // 最后肯定需要得到字符串，指针可以很好的保存数据
    static size_t Split(const std::string &src, const std::string &sep, std::vector<std::string> *ary)
    {
        size_t offset = 0;
        while (offset < src.size())
        {
            size_t pos = src.find(sep, offset);
            if (pos == src.npos)
            {
                if (pos == src.size())
                    break;
                // 可能没有sep了但是还有内容
                ary->emplace_back(src.substr(offset, pos));
                return ary->size();
            }
            if (pos != offset)
                ary->emplace_back(src.substr(offset, pos - offset));
            offset = pos + sep.size();
        }
        return ary->size();
    }

    static std::string ToLower(const std::string &str)
    {
        std::string res;
        res.reserve(str.size());
        for (unsigned char ch : str)
            res.push_back(static_cast<char>(std::tolower(ch)));
        return res;
    }

    // 读取文件内容
    // static bool ReadFile(const std::string &filename, Buffer &buf)
    static bool ReadFile(const std::string &filename, std::string &buf)
    {
        std::ifstream file(filename, std::ios::binary);
        if (!file.is_open())
        {
            ERR_LOG("open file %s failed", filename.c_str());
            return false;
        }

        // 第一种实现方式
        // std::string line;
        // while (std::getline(file, line))
        // {
        //     line += '\n'; // 必须添加，否则只有一行输出
        //     buf += line;
        // }

        // 第二种实现方式,这种比第一种感觉更安全，第一种没有读取失败的处理
        size_t fsize = 0;
        file.seekg(0, file.end); // 跳转到文件末尾
        fsize = file.tellg();    // 获取当前位置相对于起始位置的偏移量
        file.seekg(0, file.beg); // 回到文件起始位置
        buf.resize(fsize);
        if (fsize > 0)
            file.read(&buf[0], fsize);

        if (!file.good() && !file.eof())
        {
            ERR_LOG("read file %s failed", filename.c_str());
            file.close();
            return false;
        }

        file.close();
        return true;
    }
    // 向文件写入数据
    static bool WriteFile(const std::string &filename, const std::string &buf)
    {
        // 以二进制形式打开文件并清空文件
        std::ofstream file(filename.c_str(), std::ios::binary | std::ios::trunc);
        if (!file.is_open())
        {
            ERR_LOG("open file %s failed", filename.c_str());
            return false;
        }
        file.write(buf.c_str(), buf.length());
        if (!file.good())
        {
            ERR_LOG("write file %s failed", filename.c_str());
            file.close();
            return false;
        }
        file.close();
        return true;
    }
    // Url编码, 避免URL中资源路径与查询字符串中的特殊字符与HTTP请求中特殊字符产生歧义
    // 编码格式，将非数字和字母字符的ascii值转换为两个十六进制字符，前缀%
    // RFC3986规定 . - _ ~字母，数字都是不编码字符
    // W3C规定，查询字符串中的空格，需要编码成+，解码反过来
    static std::string EnCodeUrl(const std::string &url, bool convert_space_to_plus)
    {
        std::string res;
        for (const char &ch : url)
        {
            if (ch == '.' || ch == '-' || ch == '_' || ch == '~' || isalnum(ch))
            {
                res += ch;
                continue;
            }
            if (ch == ' ' && convert_space_to_plus)
            {
                res += '+';
                continue;
            }
            unsigned char uch = static_cast<unsigned char>(ch);
            char tmp[4] = {0};
            snprintf(tmp, sizeof(tmp), "%%%02X", uch);
            res += tmp;
        }
        return res;
    }
    // %2Flogin%3Fuser%26%20%20password%3Dc%2B%2B
    // Url解码
    static bool DeCodeUrl(const std::string &url, bool convert_plus_to_space, std::string &out)
    {
        out.clear();
        out.reserve(url.size());
        for (size_t i = 0; i < url.size(); ++i)
        {
            unsigned char ch = static_cast<unsigned char>(url[i]);
            if (ch == '%')
            {
                if (i + 2 >= url.size())
                    return false;
                unsigned char byte = 0;
                if (!HexPairToByte(url[i + 1], url[i + 2], byte))
                    return false;
                out.push_back(static_cast<char>(byte));
                i += 2;
            }
            else if (ch == '+' && convert_plus_to_space)
            {
                out.push_back(' ');
            }
            else
            {
                out.push_back(static_cast<char>(ch));
            }
        }
        return true;
    }

    static std::string DeCodeUrl(const std::string &url, bool convert_plus_to_space)
    {
        std::string decoded;
        if (!DeCodeUrl(url, convert_plus_to_space, decoded))
            return std::string();
        return decoded;
    }
    // 获取状态码的描述信息
    static std::string StatuCodeDesc(const int &statuCode)
    {
        static const std::unordered_map<int, std::string> statue_code_msg{
            {100, "Continue"},
            {101, "Switching Protocol"},
            {102, "Processing"},
            {103, "Early Hints"},
            {200, "OK"},
            {201, "Created"},
            {202, "Accepted"},
            {203, "Non-Authoritative Information"},
            {204, "No Content"},
            {205, "Reset Content"},
            {206, "Partial Content"},
            {207, "Multi-Status"},
            {208, "Already Reported"},
            {226, "IM Used"},
            {300, "Multiple Choice"},
            {301, "Moved Permanently"},
            {302, "Found"},
            {303, "See Other"},
            {304, "Not Modified"},
            {305, "Use Proxy"},
            {306, "unused"},
            {307, "Temporary Redirect"},
            {308, "Permanent Redirect"},
            {400, "Bad Request"},
            {401, "Unauthorized"},
            {402, "Payment Required"},
            {403, "Forbidden"},
            {404, "Not Found"},
            {405, "Method Not Allowed"},
            {406, "Not Acceptable"},
            {407, "Proxy Authentication Required"},
            {408, "Request Timeout"},
            {409, "Conflict"},
            {410, "Gone"},
            {411, "Length Required"},
            {412, "Precondition Failed"},
            {413, "Payload Too Large"},
            {414, "URI Too Long"},
            {415, "Unsupported Media Type"},
            {416, "Range Not Satisfiable"},
            {417, "Expectation Failed"},
            {418, "I'm a teapot"},
            {421, "Misdirected Request"},
            {422, "Unprocessable Entity"},
            {423, "Locked"},
            {424, "Failed Dependency"},
            {425, "Too Early"},
            {426, "Upgrade Required"},
            {428, "Precondition Required"},
            {429, "Too Many Requests"},
            {431, "Request Header Fields Too Large"},
            {451, "Unavailable For Legal Reasons"},
            {500, "Internal Server Error"},
            {501, "Not Implemented"},
            {502, "Bad Gateway"},
            {503, "Service Unavailable"},
            {504, "Gateway Timeout"},
            {505, "HTTP Version Not Supported"},
            {506, "Variant Also Negotiates"},
            {507, "Insufficient Storage"},
            {508, "Loop Detected"},
            {510, "Not Extended"},
            {511, "Network Authentication Required"}};

        auto it = statue_code_msg.find(statuCode);
        if (it == statue_code_msg.end())
            return "Unknown";
        return it->second;
    }
    // 根据文件后缀名获取mime
    static std::string ExtensionMime(const std::string &filename)
    {
        // 解析文件，得到后缀名
        std::unordered_map<std::string, std::string> ext_msg{
            {".aac", "audio/aac"},
            {".abw", "application/x-abiword"},
            {".arc", "application/x-freearc"},
            {".avi", "video/x-msvideo"},
            {".azw", "application/vnd.amazon.ebook"},
            {".bin", "application/octet-stream"},
            {".bmp", "image/bmp"},
            {".bz", "application/x-bzip"},
            {".bz2", "application/x-bzip2"},
            {".csh", "application/x-csh"},
            {".css", "text/css"},
            {".csv", "text/csv"},
            {".doc", "application/msword"},
            {".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
            {".eot", "application/vnd.ms-fontobject"},
            {".epub", "application/epub+zip"},
            {".gif", "image/gif"},
            {".htm", "text/html"},
            {".html", "text/html"},
            {".ico", "image/vnd.microsoft.icon"},
            {".ics", "text/calendar"},
            {".jar", "application/java-archive"},
            {".jpeg", "image/jpeg"},
            {".jpg", "image/jpeg"},
            {".js", "text/javascript"},
            {".json", "application/json"},
            {".jsonld", "application/ld+json"},
            {".mid", "audio/midi"},
            {".midi", "audio/x-midi"},
            {".mjs", "text/javascript"},
            {".mp3", "audio/mpeg"},
            {".mpeg", "video/mpeg"},
            {".mpkg", "application/vnd.apple.installer+xml"},
            {".odp", "application/vnd.oasis.opendocument.presentation"},
            {".ods", "application/vnd.oasis.opendocument.spreadsheet"},
            {".odt", "application/vnd.oasis.opendocument.text"},
            {".oga", "audio/ogg"},
            {".ogv", "video/ogg"},
            {".ogx", "application/ogg"},
            {".otf", "font/otf"},
            {".png", "image/png"},
            {".pdf", "application/pdf"},
            {".ppt", "application/vnd.ms-powerpoint"},
            {".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
            {".rar", "application/x-rar-compressed"},
            {".rtf", "application/rtf"},
            {".sh", "application/x-sh"},
            {".svg", "image/svg+xml"},
            {".swf", "application/x-shockwave-flash"},
            {".tar", "application/x-tar"},
            {".tif", "image/tiff"},
            {".tiff", "image/tiff"},
            {".ttf", "font/ttf"},
            {".txt", "text/plain"},
            {".vsd", "application/vnd.visio"},
            {".wav", "audio/wav"},
            {".weba", "audio/webm"},
            {".webm", "video/webm"},
            {".webp", "image/webp"},
            {".woff", "font/woff"},
            {".woff2", "font/woff2"},
            {".xhtml", "application/xhtml+xml"},
            {".xls", "application/vnd.ms-excel"},
            {".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
            {".xml", "application/xml"},
            {".xul", "application/vnd.mozilla.xul+xml"},
            {".zip", "application/zip"},
            {".3gp", "video/3gpp"},
            {".3g2", "video/3gpp2"},
            {".7z", "application/x-7z-compressed"},
        };
        size_t pos = filename.find_last_of('.');
        if (pos == filename.npos)
            return "application/octet-stream";

        auto it = ext_msg.find(filename.substr(pos));
        if (it == ext_msg.end())
            return "application/octet-stream";

        return it->second;
    }
    // 判断是否为一个目录
    static bool IsDirectory(const std::string &path)
    {
        struct stat st;
        if (stat(path.c_str(), &st) != 0)
            return false;
        return S_ISDIR(st.st_mode);
    }
    // 判断是否为一个普通文件
    static bool IsRegular(const std::string &path)
    {
        struct stat st;
        if (stat(path.c_str(), &st) != 0)
            return false;
        return S_ISREG(st.st_mode);
    }
    // 对http的请求资源路径有效性进行判断
    static bool VaildPath(const std::string &path)
    {
        // 按照/进行路径分隔，计算目录深度
        std::vector<std::string> subdir;
        Split(path, "/", &subdir);
        int level = 0;

        for (auto &dir : subdir)
        {
            if (dir == "..")
            {
                if (--level < 0)
                    return false;
            }
            else
                level++;
        }
        return true;
    }
};

class HttpRequest
{
public:
    std::string _req_method;                                  // 请求方法，get，put
    std::string _res_path;                                    // 请求资源路径
    std::string _content;                                     // 正文
    std::string _protocol_ver;                                // 协议版本
    std::unordered_map<std::string, std::string> _query_str;  // 查询字符串
    std::unordered_map<std::string, std::string> _head_field; // 头部字段
    std::smatch _matches;

public:
    HttpRequest() : _protocol_ver("HTTP/1.1") {}

public:
    void Reset()
    {
        _req_method.clear();
        _res_path.clear();
        _content.clear();
        _protocol_ver = "HTTP/1.1";
        _query_str.clear();
        _head_field.clear();
        std::smatch mat;
        _matches.swap(mat);
    }
    // 插入头部字段
    void InsertHeader(const std::string &key, const std::string &val) { _head_field[Util::ToLower(key)] = val; }

    // 判断某个头部字段是否存在
    bool HasHeader(const std::string &key) const { return _head_field.find(Util::ToLower(key)) != _head_field.end(); }
    // 获取指定头部字段的值
    std::string getHeadField(const std::string &key) const
    {
        auto it = _head_field.find(Util::ToLower(key));
        if (it == _head_field.end())
            return "";
        return it->second;
    }
    // 插入查询字符串
    void InsertQueryStr(const std::string &key, const std::string &val) { _query_str.emplace(key, val); }
    // 判断某个查询字符串是否存在
    bool HasQueryStr(const std::string &key) { return _query_str.find(key) != _query_str.end(); }
    // 获取指定的查询字符串
    std::string getQueryStr(const std::string &key)
    {
        if (HasQueryStr(key))
            return _query_str.find(key)->second;
        return "";
    }
    // 获取正文长度,查找Content-Length
    bool GetContentLength(size_t &len) const
    {
        if (!HasHeader("Content-Length"))
        {
            len = 0;
            return true;
        }
        std::string value = getHeadField("Content-Length");
        if (value.empty())
            return false;
        size_t idx = 0;
        try
        {
            unsigned long long parsed = std::stoull(value, &idx);
            if (idx != value.size())
                return false;
            if (parsed > std::numeric_limits<size_t>::max())
                return false;
            len = static_cast<size_t>(parsed);
            return true;
        }
        catch (const std::exception &)
        {
            return false;
        }
    }

    // false: LongLink
    // true: ShortLink
    // 是短连接还是长连接close还是keep-alive

    bool IsLongLinkOrShortLink()
    {
        std::string conn = Util::ToLower(getHeadField("Connection"));
        if (!conn.empty() && conn == "keep-alive")
            return false;
        return true;
    }
};

class HttpResponse
{
public:
    size_t _statue_code;
    bool _redirect_flag;
    std::unordered_map<std::string, std::string> _head_filed;
    std::string _res_content;
    std::string _redirect_url;

public:
    HttpResponse() : _statue_code(200), _redirect_flag(false) {}
    HttpResponse(size_t statu) : _statue_code(statu), _redirect_flag(false) {}

public:
    void Reset()
    {
        _statue_code = 200;
        _redirect_flag = false;
        _head_filed.clear();
        _res_content.clear();
        _redirect_url.clear();
    }
    void InsertHeader(const std::string &key, const std::string &val) { _head_filed[Util::ToLower(key)] = val; }
    bool HasHeader(const std::string &key) const { return _head_filed.find(Util::ToLower(key)) != _head_filed.end(); }
    std::string getHeadField(const std::string &key) const
    {
        auto it = _head_filed.find(Util::ToLower(key));
        if (it == _head_filed.end())
            return "";
        return it->second;
    }
    void InsertContent(const std::string &content, const std::string &type = "text/html")
    {
        _res_content = content;
        InsertHeader("Content-Type", type);
    }
    void setRedirect(std::string &url, size_t statu = 302)
    {
        _redirect_url = url;
        _statue_code = statu;
        _redirect_flag = true;
    }
    // true: LongLink
    // false: ShortLink
    // 是短连接还是长连接close还是keep-alive

    bool IsLongLinkOrShortLink()
    {
        std::string conn = Util::ToLower(getHeadField("Connection"));
        if (!conn.empty() && conn == "keep-alive")
            return false;
        return true;
    }
};

// 请求接收上下文，记录多条请求，保证得到一条完整的请求
enum class HttpRecvStatu
{
    RECV_HTTP_ERROR,
    RECV_HTTP_REQLINE,
    RECV_HTTP_HEAD,
    RECV_HTTP_CONTENT,
    RECV_HTTP_OVER,
};

class HttpContext
{
private:
    bool ParseHttpLine(const std::string &line)
    {
        std::smatch match;
        std::regex matchrule("(GET|DELETE|PUT|HEAD|POST|OPTIONS|PATCH|TRACE|CONNECT) ([^? ]*)(?:\\?([^ ]*))? (HTTP/1\\.[01])(?:\\r|\\n|\\r\\n)?");
        bool ret = std::regex_match(line, match, matchrule);
        if (!ret)
        {
            _recv = HttpRecvStatu::RECV_HTTP_ERROR;
            _resp_statu = 400;
            return false;
        }
        _request._req_method = match[1];
        std::transform(_request._req_method.begin(), _request._req_method.end(),
                       _request._req_method.begin(), ::toupper);
        std::string decoded_path;
        if (!Util::DeCodeUrl(match[2], false, decoded_path))
        {
            _recv = HttpRecvStatu::RECV_HTTP_ERROR;
            _resp_statu = 400;
            return false;
        }
        _request._res_path = decoded_path;
        _request._protocol_ver = match[4];
        std::vector<std::string> vec;
        std::string decoded_query;
        if (match.size() > 3 && match[3].matched && !match[3].str().empty())
        {
            if (!Util::DeCodeUrl(match[3], true, decoded_query))
            {
                _recv = HttpRecvStatu::RECV_HTTP_ERROR;
                _resp_statu = 400;
                return false;
            }
            Util::Split(decoded_query, "&", &vec);
        }
        for (std::string &str : vec)
        {
            size_t pos = str.find("=");
            if (pos == str.npos)
            {
                if (!str.empty())
                    _request.InsertQueryStr(str, "");
                continue;
            }
            _request.InsertQueryStr(str.substr(0, pos), pos + 1 < str.size() ? str.substr(pos + 1) : "");
        }
        return true;
    }
    bool RecvHttpLine(Buffer *buf)
    {
        if (_recv != HttpRecvStatu::RECV_HTTP_REQLINE)
            return false;
        // 1. 获取一行数据
        std::string line = buf->GetLine();
        // 2. 需要考虑的一些因素：缓冲区的数据不足一行，获取的一行数据超大
        // 没有读出来一行
        if (line.size() == 0)
        {
            if (buf->ReadableSpace() > MAX_LINE)
            {
                _resp_statu = 414;
                _recv = HttpRecvStatu::RECV_HTTP_ERROR;
                return false;
            }
            // 数据不多，就等数据
            return true;
        }
        // 数据很大
        if (line.size() > MAX_LINE)
        {
            _resp_statu = 414;
            _recv = HttpRecvStatu::RECV_HTTP_ERROR;
            return false;
        }
        if (!ParseHttpLine(line))
            return false;
        buf->MoveReadOffset(line.length());
        _recv = HttpRecvStatu::RECV_HTTP_HEAD;
        return true;
    }

    bool RecvHttpHead(Buffer *buf)
    {
        if (_recv != HttpRecvStatu::RECV_HTTP_HEAD)
            return false;
        bool header_complete = false;
        while (1)
        {
            // 1. 获取一行数据
            std::string line = buf->GetLine();
            // 2. 需要考虑的一些因素：缓冲区的数据不足一行，获取的一行数据超大
            // 没有读出来一行
            if (line.size() == 0)
            {
                if (buf->ReadableSpace() > MAX_LINE)
                {
                    _resp_statu = 414;
                    _recv = HttpRecvStatu::RECV_HTTP_ERROR;
                    return false;
                }
                // 数据不多，就等数据
                return true;
            }
            // 数据很大
            if (line.size() > MAX_LINE)
            {
                _resp_statu = 414; // URI too long
                _recv = HttpRecvStatu::RECV_HTTP_ERROR;
                return false;
            }
            size_t line_len = line.length();
            if (line == "\n" || line == "\r\n")
            {
                buf->MoveReadOffset(line_len);
                _recv = HttpRecvStatu::RECV_HTTP_CONTENT;
                header_complete = true;
                break;
            }
            if (!ParseHttpHead(line))
                return false;

            buf->MoveReadOffset(line_len);
        }
        if (!header_complete)
            return true;

        std::string transfer_encoding = Util::ToLower(_request.getHeadField("Transfer-Encoding"));
        if (!transfer_encoding.empty())
        {
            if (_request.HasHeader("Content-Length"))
            {
                _resp_statu = 400;
                _recv = HttpRecvStatu::RECV_HTTP_ERROR;
                return false;
            }
            _resp_statu = 501;
            _recv = HttpRecvStatu::RECV_HTTP_ERROR;
            return false;
        }

        return true;
    }

    bool ParseHttpHead(std::string &line)
    {
        while (!line.empty() && (line.back() == '\n' || line.back() == '\r'))
            line.pop_back();
        if (line.empty())
        {
            _recv = HttpRecvStatu::RECV_HTTP_ERROR;
            _resp_statu = 400; // bad request
            return false;
        }
        size_t pos = line.find(':');
        if (pos == line.npos)
        {
            _recv = HttpRecvStatu::RECV_HTTP_ERROR;
            _resp_statu = 400; // bad request
            return false;
        }
        std::string key = line.substr(0, pos);
        std::string value = line.substr(pos + 1);
        while (!key.empty() && isspace(static_cast<unsigned char>(key.back())))
            key.pop_back();
        while (!value.empty() && isspace(static_cast<unsigned char>(value.front())))
            value.erase(value.begin());
        while (!value.empty() && isspace(static_cast<unsigned char>(value.back())))
            value.pop_back();
        if (key.empty())
        {
            _recv = HttpRecvStatu::RECV_HTTP_ERROR;
            _resp_statu = 400; // bad request
            return false;
        }
        _request.InsertHeader(key, value);
        return true;
    }

    bool RecvHttpContent(Buffer *buf)
    {
        if (_recv != HttpRecvStatu::RECV_HTTP_CONTENT)
            return false;
        size_t len = 0;
        if (!_request.GetContentLength(len))
        {
            _resp_statu = 400;
            _recv = HttpRecvStatu::RECV_HTTP_ERROR;
            return false;
        }
        if (len == 0)
        {
            _recv = HttpRecvStatu::RECV_HTTP_OVER;
            return true;
        }
        if (_request._content.length() > len)
        {
            _resp_statu = 400;
            _recv = HttpRecvStatu::RECV_HTTP_ERROR;
            return false;
        }
        // 实际还需要接收的长度
        size_t real_len = len - _request._content.length();
        if (real_len == 0)
        {
            _recv = HttpRecvStatu::RECV_HTTP_OVER;
            return true;
        }
        // 当前可读的数据比实际需要的多
        else if (buf->ReadableSpace() >= real_len)
        {
            _request._content.append(buf->getReadOffset(), real_len);
            buf->MoveReadOffset(real_len);
            _recv = HttpRecvStatu::RECV_HTTP_OVER;
            return true;
        }
        size_t readable = buf->ReadableSpace();
        _request._content.append(buf->getReadOffset(), readable);
        buf->MoveReadOffset(readable);
        return true;
    }

public:
    HttpContext() : _resp_statu(200), _recv(HttpRecvStatu::RECV_HTTP_REQLINE) {}
    void ReSet()
    {
        _resp_statu = 200;
        _recv = HttpRecvStatu::RECV_HTTP_REQLINE;
        _request.Reset();
    }

    size_t RespStatu() { return _resp_statu; }
    HttpRecvStatu RecvStatu() { return _recv; }
    HttpRequest &Request() { return _request; }

    void RecvHttpRequest(Buffer *buf)
    {
        switch (_recv)
        {
        // 不需要break，这是一个流程，解析完请求行，解析头部，然后解析内容
        case HttpRecvStatu::RECV_HTTP_REQLINE:
            this->RecvHttpLine(buf);
        case HttpRecvStatu::RECV_HTTP_HEAD:
            this->RecvHttpHead(buf);
        case HttpRecvStatu::RECV_HTTP_CONTENT:
            this->RecvHttpContent(buf);
        }
    }

private:
    size_t _resp_statu;   // 响应码状态
    HttpRequest _request; // 当前解析到的请求信息
    HttpRecvStatu _recv;  // 当前接受及解析的阶段状态
};

constexpr size_t TIME_OUT = 10;

class HttpServer
{
private:
    using Handler = std::function<void(const HttpRequest &, HttpResponse &)>;
    std::vector<std::pair<std::regex, Handler>> _get_route;
    std::vector<std::pair<std::regex, Handler>> _put_route;
    std::vector<std::pair<std::regex, Handler>> _post_route;
    std::vector<std::pair<std::regex, Handler>> _delete_route;
    std::string _base_dir; // 静态资源根目录
    TcpServer _server;

private:
    void ErrorHandler(const HttpRequest &req, HttpResponse &resp)
    {
        // 1. 组织一个错误展示页面
        std::string body;
        body += "<html>";
        body += "<head>";
        body += "<meta http-equiv='Content-Type' content='text/html;charset=utf-8'>";
        body += "</head>";
        body += "<body>";
        body += "<h1>";
        body += std::to_string(resp._statue_code);
        body += " ";
        body += Util::StatuCodeDesc(resp._statue_code);
        body += "</h1>";
        body += "</body>";
        body += "</html>";
        // 2. 将页面数据，当作响应正文，放入rsp中
        resp.InsertContent(body, "text/html");
    }

    // 将HttpResponse的各种要素按照http格式进行组织，发送
    void WriteReponse(const PtrConnection &conn, HttpRequest &req, HttpResponse &resp)
    {
        // 1. 先完善头部字段
        if (req.IsLongLinkOrShortLink() == false)
            resp.InsertHeader("Connection", "keep-alive");
        else
            resp.InsertHeader("Connection", "close");

        if (resp._res_content.empty() == false && resp.HasHeader("Content-Length") == false)
            resp.InsertHeader("Content-Length", std::to_string(resp._res_content.length()));

        if (resp._res_content.empty() == false && resp.HasHeader("Content-Type") == false)
            resp.InsertHeader("Content-Type", "appliaction/octet-stream");

        if (resp._redirect_flag == true)
            resp.InsertHeader("Location", resp._redirect_url);
        // 2. 将resp中的要素，按照Http协议格式组织
        std::stringstream resp_str;
        resp_str << req._protocol_ver << " " << std::to_string(resp._statue_code) << " " << Util::StatuCodeDesc(resp._statue_code) << "\r\n";
        for (auto &it : resp._head_filed)
            resp_str << it.first << ": " << it.second << "\r\n";
        resp_str << "\r\n";
        resp_str << resp._res_content;
        // 3. 发送
        conn->Send(resp_str.str().c_str(), resp_str.str().length());
    }

    // 静态资源的请求处理，把数据读取出来，放在resp的正文里，并设置mime
    bool FileHandler(const HttpRequest &req, HttpResponse &resp)
    {
        std::string req_path = _base_dir + req._res_path;
        if (req._res_path.back() == '/')
            req_path += "index.html";
        bool ret = Util::ReadFile(req_path, resp._res_content);
        if (!ret)
            return false;

        std::string mime = Util::ExtensionMime(req_path);
        resp.InsertHeader("Content-Type", mime);
        return true;
    }

    // 功能性请求的分发处理
    void Dispatcher(std::vector<std::pair<std::regex, Handler>> &routetable, HttpRequest &req, HttpResponse &resp)
    {
        // 在对应请求的路由表中，查找是否有对应资源的请求资源处理函数，没有返回404
        // 遍历存储的路由表即可
        // 路由表存储的是键值对：正则表达式 & 处理函数
        for (auto &it : routetable)
        {
            // regex会对字符串进行编译，得到一个容易解析的格式，这样每次请求都进行一次编译，会降低效率，
            // 所以把原来的string:handler改为regex:handler,可以提高效率
            const std::regex &re = it.first;
            Handler &handle = it.second;
            // std::smatch matches;
            bool ret = std::regex_match(req._res_path, req._matches, re);
            if (!ret)
                continue;
            // // copy request so we can attach the matches (original req is const)
            // HttpRequest req_copy = req;
            // req_copy._matches = matches;
            return handle(req, resp);
        }
        resp._statue_code = 404; // 对应的请求没有处理的方法
    }

    bool IsFileHandle(const HttpRequest &req)
    {
        // 1. 必须设置了静态资源根目录
        // 2. 请求方法，必须是GET/HEAD请求方法
        // 3. 请求的资源路径必须是一个合法路径
        if (_base_dir.empty() ||
            !Util::VaildPath(req._res_path) ||
            (req._req_method != "GET" && req._req_method != "HEAD"))
            return false;
        // 4. 请求的资源必须存在,且必须是普通文件还有一种纯粹的相对根目录，这种默认追加index.thml
        std::string req_path = _base_dir + req._res_path;
        if (req._res_path.back() == '/')
            req_path += "index.html";
        if (!Util::IsRegular(req_path))
            return false;
        return true;
    }

    void Route(HttpRequest &req, HttpResponse &resp)
    {
        // 静态资源处理完后直接返回
        // 功能性请求资源处理
        // 二者都不是，返回404
        if (IsFileHandle(req))
            FileHandler(req, resp);
        std::string method = req._req_method;
        if (method == "GET" || method == "HEAD")
            return Dispatcher(_get_route, req, resp);
        else if (method == "PUT")
            return Dispatcher(_put_route, req, resp);
        else if (method == "POST")
            return Dispatcher(_post_route, req, resp);
        else if (method == "DELETE")
            return Dispatcher(_delete_route, req, resp);

        resp._statue_code = 405; // Method not allowed
    }

    // 设置上下文
    void OnConnected(const PtrConnection &conn)
    {
        conn->SetContext(HttpContext());
        DBG_LOG("New Connection %p", conn.get());
    }

    // 接收缓冲区数据解析+处理
    void OnMessage(const PtrConnection &conn, Buffer *buf)
    {
        while (buf->ReadableSpace() > 0)
        {
            // 1. 获取上下文
            HttpContext *content = conn->getContext()->get<HttpContext>();
            // 2. 通过上下文对缓冲区数据进行解析，得到HttpRequest对象
            content->RecvHttpRequest(buf);
            HttpRequest &req = content->Request();
            HttpResponse resp(content->RespStatu());
            if (content->RespStatu() >= 400)
            {
                ErrorHandler(req, resp);
                WriteReponse(conn, req, resp);
                content->ReSet();
                // 出错了就把缓冲区数据清空
                buf->MoveReadOffset(buf->ReadableSpace());
                conn->ShutDown();
                return;
            }
            if (content->RecvStatu() != HttpRecvStatu::RECV_HTTP_OVER)
                return;
            // 3. 请求路由 + 业务处理
            Route(req, resp);
            // 4. 对HttpResponse进行组织发送,conn是哪个连接去发送
            WriteReponse(conn, req, resp);
            // 5. 重置上下文
            content->ReSet();
            // 6. 根据长短链接判断接下来怎么处理
            if (resp.IsLongLinkOrShortLink())
                conn->ShutDown();
        }
    }

public:
    HttpServer(int port, int timeout = TIME_OUT)
        : _server(port)
    {
        _server.EnableInactiveRelease(timeout);
        _server.SetConnectedCallBack(std::bind(&HttpServer::OnConnected, this, std::placeholders::_1));
        _server.SetMessageCallBack(std::bind(&HttpServer::OnMessage, this, std::placeholders::_1, std::placeholders::_2));
    }
    void Get(const std::string &pattern, const Handler &handle) { _get_route.emplace_back(std::regex(pattern), handle); }
    void Put(const std::string &pattern, const Handler &handle) { _put_route.emplace_back(std::regex(pattern), handle); }
    void Post(const std::string &pattern, const Handler &handle) { _post_route.emplace_back(std::regex(pattern), handle); }
    void Delete(const std::string &pattern, const Handler &handle) { _delete_route.emplace_back(std::regex(pattern), handle); }
    void setThreadCount(size_t count) { _server.setThreadCount(count); }
    void setBaseDir(std::string basedir)
    {
        assert(Util::IsDirectory(basedir) == true);
        _base_dir = basedir;
    }
    void Listen() { _server.Start(); }
};

#endif